<?php

namespace Ma\GitlabTag;

use GuzzleHttp\Client;
use Ma\GitlabTag\Exceptions\HttpException;
use Ma\GitlabTag\Exceptions\NotFoundException;

class TagClient
{

    private $token = '';

    private $config = [];

    protected $client;

    public function __construct($projectName)
    {
        $config        = include __DIR__ . '/config.php';
        $projectConfig = $config['config'][$projectName] ?? [];
        if (!$config || !$projectConfig || !$config['token'] || !$config['gitlab_host']) {
            throw new NotFoundException('没有找到项目配置');
        }
        $host         = $config['gitlab_host'];
        $this->config = $projectConfig;
        $this->token  = $config['token'];
        $this->client = new Client(['base_uri' => $host . '/api/v4/projects/' . $this->config['project_id'] . '/']);
    }

    /**
     * 获取tag列表
     * @return array|mixed
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function getTags(): array
    {
        $response = $this->client->request('GET', 'repository/tags', [
            'headers' => [
                'PRIVATE-TOKEN' => $this->token,
            ],
        ]);

        if ($response->getStatusCode() !== 200) {
            throw new HttpException('get tags request fail',print_r($response->getBody()));
        }

        return json_decode($response->getBody(), true) ?? [];
    }

    /**
     * 根据tag名字获取tag的信息
     * @param string $tagName
     * @return array|mixed
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function getTag(string $tagName): array
    {
        $response = $this->client->request('GET', 'repository/tags/' . $tagName, [
            'headers' => [
                'PRIVATE-TOKEN' => $this->token,
            ],
        ]);

        if ($response->getStatusCode() !== 200) {
            throw new HttpException('get tag request fail',print_r($response->getBody()));
        }

        return json_decode($response->getBody(), true) ?? [];
    }

    /**
     * 自动创建tag，tag名字 = 当前最新的tag名字 + 1
     * @return array [result,tagName]
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function createTagByAutoName(): array
    {
        $lastestTagVersion = $this->getLastestTagVersion();
        $lastestTagName    = $this->getFullTagName($lastestTagVersion);
        $lastestTagInfo = $this->getTag($lastestTagName);
        if (!$lastestTagInfo) {
            throw new NotFoundException('not found lastest tag' . $lastestTagName);
        }

        $nextTagVersion = $this->getNextTagVersion($lastestTagVersion);
        if (!$nextTagVersion) {
            return [[], $nextTagVersion];
        }
        return [$this->createTag($nextTagVersion), $nextTagVersion];
    }

    /**
     * 创建tag
     * @param string $version 版本号
     * @param string $message 内容
     * @param string $desc 描述
     * @return array|mixed
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function createTag($version, $message = '', $desc = ''): array
    {
        $tagName = $this->getFullTagName($version);
        $response = $this->client->request('POST', 'repository/tags/', [
            'headers' => [
                'PRIVATE-TOKEN' => $this->token,
            ],
            'query'   => [
                'tag_name'            => $tagName,
                'ref'                 => $this->config['branch'],
                'message'             => $message,
                'release_description' => $desc,
            ],
        ]);
        $httpStatusCode = $response->getStatusCode();
        if ($httpStatusCode < 200 || $httpStatusCode >= 300) {
            throw new HttpException('create tag request fail',print_r($response->getBody()));
        }

        return json_decode($response->getBody(), true) ?? [];
    }

    /**
     * 获取最新版本号
     * @return mixed|string
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    private function getLastestTagVersion(): string
    {
        $tags       = $this->getTags();
        $maxVersion = '0.0.0';
        $prefixLen  = strlen($this->config['tag_name_prefix']);
        foreach ($tags as $tag) {
            $tagName = $tag['name'];
            if (!preg_match("/{$this->config['tag_name_prefix']}/i", $tag['name'])) {
                continue;
            }
            $version    = substr($tagName, $prefixLen);
            $maxVersion = $this->getMaxVersion($maxVersion, $version);
        }
        return $maxVersion;
    }

    /**
     * 根据版本号获取下一个版本号
     * @param string $tagName 版本号
     * @return string
     */
    private function getNextTagVersion(string $tagName): string
    {
        $tagArr        = explode('.', $tagName);
        $versionLength = count($tagArr);
        if ($versionLength > 3 || $versionLength == 0) {
            return '';
        }
        if ($tagArr[2] == 99) {
            $tagArr[1] += 1;
            $tagArr[2] = '01';
        } else {
            $tagArr[2] = (int)$tagArr[2];
            $tagArr[2] += 1;
            $tagArr[2] = str_pad($tagArr[2], 2, '0', STR_PAD_LEFT);
        }
        return implode('.', $tagArr);
    }

    /**
     * 完整tag名称
     * @param $version
     * @return string
     */
    private function getFullTagName(string $version): string
    {
        return $this->config['tag_name_prefix'] . $version;
    }

    /**
     * 比较版本号，返回最大的版本号
     * @param string $v1
     * @param string $v2
     * @return mixed
     */
    private function getMaxVersion(string $v1, string $v2): string
    {
        $v1Arr = explode('.', $v1);
        $v2Arr = explode('.', $v2);
        for ($i = 0; $i < 3; $i++) {
            if ($v1Arr[$i] > $v2Arr[$i]) {
                return $v1;
            }
            if ($v1Arr[$i] < $v2Arr[$i]) {
                return $v2;
            }
        }
        return $v1;
    }
}

